﻿using System;
using System.Collections;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.IO.Compression;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Text;
using System.Resources;
using System.Collections.Specialized;
using System.Reflection;
using System.Threading;
using System.Xml;
using MapleLib.WzLib;
using MapleLib.WzLib.Util;
using MapleLib.WzLib.WzProperties;
using System.Text.RegularExpressions;

namespace MapleLib.Examples.WzExtractor
{
	public partial class MainForm : Form
	{
		public WzListFile ListWzFile;
		public int searchidx = 0;
		public int currentidx = 0;
		public List<WzFile> WzFiles = new List<WzFile>();
		public bool finished = false;
		public bool usebasepng = false;
		public bool combineimgs = false;
		public int saveObjectType = -1;

		public MainForm()
		{
			InitializeComponent();
		}

		private void button1_Click(object sender, EventArgs e)
		{
			OpenFileDialog wzopen = new OpenFileDialog();
			wzopen.Title = "Select the wz file...";
			wzopen.Filter = "MapleStory wz file|*.wz|MapleStory img file|*.img";
			if (wzopen.ShowDialog() != DialogResult.OK)
			{
				return;
			}
			string filename = Path.GetFileName(wzopen.FileName);
			if (filename == "List.wz")
			{
				LoadListWzFile(wzopen.FileName);
			}
			else if (filename.EndsWith(".img"))
			{
				LoadWzImageFile(wzopen.FileName, filename);
			}
			else
			{
				LoadWzFile(wzopen.FileName);
			}
		}

		private void RecursiveTV(WzDirectory dir, TreeNode lastnode)
		{
			foreach (WzDirectory subdir in dir.WzDirectories)
			{
				TreeNode newnode = lastnode.Nodes.Add(subdir.Name);
				newnode.Tag = subdir;
				RecursiveTV(subdir, newnode);
			}
			foreach (WzImage subimg in dir.WzImages)
			{
				TreeNode newnode = lastnode.Nodes.Add(subimg.Name);
				newnode.Tag = subimg;
			}
		}

		private TreeNode GetNode(string name)
		{
			foreach (TreeNode node in dataTreeView.Nodes)
			{
				if (node.Name == name)
				{
					return node;
				}
			}
			return null;
		}

		private void ExtractSubProperty(IWzImageProperty[] subprop, TreeNode parent)
		{
			foreach (IWzImageProperty prop in subprop)
			{
				TreeNode newnode = parent.Nodes.Add(prop.Name);
				newnode.Tag = prop;
				if (prop.WzProperties != null && prop.WzProperties.Length > 0)
				{
					ExtractSubProperty(prop.WzProperties, newnode);
				}
			}
		}

		private void ExtractImg(WzImage img, TreeNode parent)
		{
			ExtractSubProperty(img.WzProperties, parent);
		}

		private void treeView1_DoubleClick(object sender, EventArgs e)
		{
			if (dataTreeView.SelectedNode == null || dataTreeView.SelectedNode.Nodes.Count != 0)
				return;
			object data = dataTreeView.SelectedNode.Tag;
			if (data == null)
				return;
			if (data is WzImage)
			{
				ExtractImg((WzImage)data, dataTreeView.SelectedNode);
				dataTreeView.SelectedNode.Expand();
				Sort();
			}
		}

		private void treeView1_AfterSelect(object sender, TreeViewEventArgs e)
		{
			saveObjectButton.Visible = false;
			saveObjectType = -1;
			saveObjectButton.Text = "Save Object";
			pictureBox1.Image = null;
			object data = dataTreeView.SelectedNode.Tag;
			if (data == null)
			{
				dataTextBox.Visible = false;
				return;
			}

			if (data is IWzObject)
			{
				IWzObject dataObject = (IWzObject)data;
				SelectionTypeLabel.Text = "Selection Type: " + dataObject.ObjectType.ToString();
				if (dataObject.ObjectType == WzObjectType.Image)
				{
					saveObjectButton.Visible = true;
					saveObjectType = 2;
					saveObjectButton.Text = "Save IMG";
				}
				dataTextBox.Visible = false;
				if (dataObject.ObjectType == WzObjectType.Property)
				{
					IWzImageProperty dataProperty = (IWzImageProperty)dataObject;
					SelectionTypeLabel.Text = "Selection Type: " + dataProperty.PropertyType.ToString();
					switch (dataProperty.PropertyType)
					{
						case WzPropertyType.CompressedInt:
							dataTextBox.Text = Convert.ToString(((WzCompressedIntProperty)data).Value);
							dataTextBox.Visible = true;
							break;
						case WzPropertyType.Canvas:
							saveObjectButton.Visible = true;
							saveObjectType = 0;
							saveObjectButton.Text = "Save PNG";
							try
							{
								pictureBox1.Image = ((WzCanvasProperty)data).PngProperty.PNG;
							}
							catch
							{
								pictureBox1.Image = null;
							}
							break;
						case WzPropertyType.Vector:
							dataTextBox.Visible = true;
							dataTextBox.Text = "X: " + Convert.ToString(((WzVectorProperty)data).X.Value) + "\r\n" + "Y: " + Convert.ToString(((WzVectorProperty)data).Y.Value);
							break;
						case WzPropertyType.String:
							dataTextBox.Visible = true;
							dataTextBox.Text = ((WzStringProperty)data).Value;
							break;
						case WzPropertyType.UOL:
							dataTextBox.Text = "Link (UOL) to " + ((WzUOLProperty)data).Value;
							dataTextBox.Visible = true;
							break;
						case WzPropertyType.Sound:
							saveObjectButton.Visible = true;
							saveObjectType = 1;
							saveObjectButton.Text = "Save MP3";
							break;
						case WzPropertyType.Double:
							dataTextBox.Visible = true;
							dataTextBox.Text = Convert.ToString(((WzDoubleProperty)data).Value);
							break;
						case WzPropertyType.ByteFloat:
							dataTextBox.Visible = true;
							dataTextBox.Text = Convert.ToString(((WzByteFloatProperty)data).Value);
							break;
						case WzPropertyType.UnsignedShort:
							dataTextBox.Visible = true;
							dataTextBox.Text = Convert.ToString(((WzUnsignedShortProperty)data).Value);
							break;
					}
				}
			}
		}

		private void SaveObjectbutton_Click(object sender, EventArgs e)
		{
			if (saveObjectType == 0)
			{
				SaveFileDialog pngsave = new SaveFileDialog();
				pngsave.Title = "Select where to save...";
				pngsave.Filter = "PNG file|*.png";
				if (!(pngsave.ShowDialog() == DialogResult.OK) || !(Path.GetExtension(pngsave.FileName) == ".png"))
				{
					return;
				}
				pictureBox1.Image.Save(pngsave.FileName);
			}
			else if (saveObjectType == 1)
			{
				SaveFileDialog mp3save = new SaveFileDialog();
				mp3save.Title = "Select where to save...";
				mp3save.Filter = "MP3 file|*.mp3";
				if (!(mp3save.ShowDialog() == DialogResult.OK) || !(Path.GetExtension(mp3save.FileName) == ".mp3"))
				{
					return;
				}
				byte[] data = ((WzSoundProperty)dataTreeView.SelectedNode.Tag).SoundData;
				FileStream mp3 = File.OpenWrite(mp3save.FileName);
				mp3.Write(data, 0, data.Length);
				mp3.Close();
			}
			else if (saveObjectType == 2)
			{
				WzImage imgFile = ((WzImage)dataTreeView.SelectedNode.Tag);
				SaveFileDialog imgsave = new SaveFileDialog();
				imgsave.Title = "Select where to save...";
				imgsave.Filter = "IMG file|*.img";
				imgsave.FileName = imgFile.Name;
				if (!(imgsave.ShowDialog() == DialogResult.OK) || !(Path.GetExtension(imgsave.FileName) == ".img"))
				{
					return;
				}
				byte[] data = imgFile.DataBlock;
				MessageBox.Show(BitConverter.ToString(data));
				FileStream img = File.OpenWrite(imgsave.FileName);
				img.Write(data, 0, data.Length);
				img.Close();
			}
		}

		private WzMapleVersion SelectedMapleVersion()
		{
			return WzTool.StringToEnum<WzMapleVersion>(MapleVersionComboBox.SelectedText);
		}

		public WzFile LoadWzFile(string path)
		{
			WzFile wzf = new WzFile(path, SelectedMapleVersion());
			wzf.ParseWzFile();
			TreeNode newnode = dataTreeView.Nodes.Add(wzf.WzDirectory.Name);
			newnode.Tag = wzf;
			RecursiveTV(wzf.WzDirectory, newnode);
			WzFiles.Add(wzf);
			Sort();
			return wzf;
		}

		public WzListFile LoadListWzFile(string path)
		{

			WzListFile listWz = new WzListFile(path, WzTool.GetIvByMapleVersion(SelectedMapleVersion()));
			listWz.ParseWzFile();
			TreeNode newnode = dataTreeView.Nodes.Add("List.wz");
			foreach (string entry in listWz.WzListEntries)
			{
				newnode.Nodes.Add(entry);
			}
			return listWz;
		}

		public WzImage LoadWzImageFile(string path, string name)
		{
			WzImage img = new WzImage(name, File.Open(path, FileMode.Open), SelectedMapleVersion());
			img.Offset = 0;
			img.ParseImage();
			TreeNode parentnode = dataTreeView.Nodes.Add(img.Name);
			parentnode.Tag = img;
			ExtractImg(img, parentnode);
			Sort();
			return img;
		}

		private void Sort()
		{
			if (sortCheckbox.Checked)
			{
				dataTreeView.Sort();
			}
		}

		private void Form1_Load(object sender, EventArgs e)
		{
			MapleVersionComboBox.SelectedIndex = 0;
		}

		public void DumpXML(TextWriter tw, string depth, IWzImageProperty[] props)
		{
			foreach (IWzImageProperty property in props)
			{
				if (property != null)
				{
					switch (property.PropertyType)
					{
						case WzPropertyType.Canvas:
							WzCanvasProperty canvasProp = (WzCanvasProperty)property;
							if (usebasepng)
							{
								MemoryStream stream = new MemoryStream();
								canvasProp.PngProperty.PNG.Save(stream, System.Drawing.Imaging.ImageFormat.Png);
								byte[] pngbytes = stream.ToArray();
								stream.Close();
								tw.WriteLine(string.Concat(new object[] { depth, "<canvas name=\"", property.Name, "\" width=\"", canvasProp.PngProperty.Width, "\" height=\"", canvasProp.PngProperty.Height, "\" basedata=\"", Convert.ToBase64String(pngbytes), "\">" }));
							}
							else
							{
								tw.WriteLine(string.Concat(new object[] { depth, "<canvas name=\"", property.Name, "\" width=\"", canvasProp.PngProperty.Width, "\" height=\"", canvasProp.PngProperty.Height, "\">" }));
							}
							this.DumpXML(tw, depth + "    ", canvasProp.WzProperties);
							tw.WriteLine(depth + "</canvas>");
							break;
						case WzPropertyType.CompressedInt:
							WzCompressedIntProperty compIntProp = (WzCompressedIntProperty)property;
							tw.WriteLine(string.Concat(new object[] { depth, "<int name=\"", property.Name, "\" value=\"", compIntProp.Value, "\"/>" }));
							break;
						case WzPropertyType.Double:
							WzDoubleProperty doubleProp = (WzDoubleProperty)property; ;
							tw.WriteLine(string.Concat(new object[] { depth, "<double name=\"", property.Name, "\" value=\"", doubleProp.Value, "\"/>" }));
							break;
						case WzPropertyType.Null:
							WzNullProperty nullProp = (WzNullProperty)property;
							tw.WriteLine(depth + "<null name=\"" + property.Name + "\"/>");
							break;
						case WzPropertyType.Sound:
							WzSoundProperty soundProp = (WzSoundProperty)property;
							tw.WriteLine(depth + "<sound name=\"" + property.Name + "\"/>");
							break;
						case WzPropertyType.String:
							WzStringProperty stringProp = (WzStringProperty)property;
							string str = stringProp.Value.Replace("<", "&lt;").Replace("&", "&amp;").Replace(">", "&gt;").Replace("'", "&apos;").Replace("\"", "&quot;");
							tw.WriteLine(depth + "<string name=\"" + property.Name + "\" value=\"" + str + "\"/>");
							break;
						case WzPropertyType.SubProperty:
							WzSubProperty subProp = (WzSubProperty)property;
							tw.WriteLine(depth + "<imgdir name=\"" + property.Name + "\">");
							this.DumpXML(tw, depth + "    ", subProp.WzProperties);
							tw.WriteLine(depth + "</imgdir>");
							break;
						case WzPropertyType.UnsignedShort:
							WzUnsignedShortProperty ushortProp = (WzUnsignedShortProperty)property;
							tw.WriteLine(string.Concat(new object[] { depth, "<short name=\"", property.Name, "\" value=\"", ushortProp.Value, "\"/>" }));
							break;
						case WzPropertyType.UOL:
							WzUOLProperty uolProp = (WzUOLProperty)property;
							tw.WriteLine(depth + "<uol name=\"" + property.Name + "\" value=\"" + uolProp.Value + "\"/>");
							break;
						case WzPropertyType.Vector:
							WzVectorProperty vectorProp = (WzVectorProperty)property;
							tw.WriteLine(string.Concat(new object[] { depth, "<vector name=\"", property.Name, "\" x=\"", vectorProp.X.Value, "\" y=\"", vectorProp.Y.Value, "\"/>" }));
							break;
						case WzPropertyType.ByteFloat:
							WzByteFloatProperty floatProp = (WzByteFloatProperty)property;
							string str2 = floatProp.Value.ToString();
							if (!str2.Contains("."))
							{
								str2 = str2 + ".0";
							}
							tw.WriteLine(depth + "<float name=\"" + property.Name + "\" value=\"" + str2 + "\"/>");
							break;
						case WzPropertyType.Convex:
							tw.WriteLine(depth + "<extended name=\"" + property.Name + "\">");
							DumpXML(tw, depth + "    ", ((WzConvexProperty)property).WzProperties);
							tw.WriteLine(depth + "</extended>");
							break;
					}
				}
			}
		}

		public void DumpDir(WzDirectory dir, string directory)
		{
			if (dir != null)
			{
				if (!combineimgs)
				{
					foreach (WzDirectory directory2 in dir.WzDirectories)
					{
						Directory.CreateDirectory(directory + "/" + directory2.Name);
						this.DumpDir(directory2, directory + "/" + directory2.Name);
					}
					foreach (WzImage image in dir.WzImages)
					{
						TextWriter tw = new StreamWriter(directory + "/" + image.Name + ".xml");
						tw.WriteLine("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>");
						tw.WriteLine("<imgdir name=\"" + image.Name + "\">");
						this.DumpXML(tw, "    ", image.WzProperties);
						tw.WriteLine("</imgdir>");
						tw.Close();
					}
				}
				else
				{
					TextWriter tw = new StreamWriter(directory + ".xml");
					tw.WriteLine("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>");
					tw.WriteLine("<xmldump>");
					foreach (WzDirectory directory2 in dir.WzDirectories)
					{
						this.DumpDirRecursive(tw, directory2, "    ");
					}
					foreach (WzImage image in dir.WzImages)
					{
						tw.WriteLine("    " + "<wzimg name=\"" + image.Name + "\">");
						this.DumpXML(tw, "    " + "    ", image.WzProperties);
						tw.WriteLine("    " + "</wzimg>");
					}
					tw.WriteLine("</xmldump>");
					tw.Close();
				}
			}
		}

		private void DumpDirRecursive(TextWriter tw, WzDirectory dir, string depth)
		{
			tw.WriteLine(depth + "<wzdir name=\"" + dir.Name + "\">");
			foreach (WzDirectory subdir in dir.WzDirectories)
			{
				DumpDirRecursive(tw, subdir, depth + "    ");
			}
			foreach (WzImage image in dir.WzImages)
			{
				tw.WriteLine(depth + "    " + "<wzimg name=\"" + image.Name + "\">");
				this.DumpXML(tw, depth + "    " + "    ", image.WzProperties);
				tw.WriteLine(depth + "    " + "</wzimg>");
			}
			tw.WriteLine(depth + "</wzdir>");
		}

		private void dumpToolStripMenuItem_Click(object sender, EventArgs e)
		{
			usebasepng = false;
			combineimgs = false;
			if (PNGIncludedCheckbox.Checked)
			{
				usebasepng = true;
			}
			if (CombineImgXmlCheckbox.Checked)
			{
				combineimgs = true;
			}
			if (dataTreeView.SelectedNode == null || dataTreeView.SelectedNode.Tag == null)
			{
				MessageBox.Show("no img selected");
				return;
			}
			if (dataTreeView.SelectedNode.Tag is WzImage)
			{
				WzImage current = (WzImage)dataTreeView.SelectedNode.Tag;
				TextWriter tw = new StreamWriter(current.Name + ".xml");
				tw.WriteLine("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>");
				tw.WriteLine("<imgdir name=\"" + current.Name + "\">");
				DumpXML(tw, "    ", current.WzProperties);
				tw.WriteLine("</imgdir>");
				tw.Close();
			}
			else if (dataTreeView.SelectedNode.Tag is WzDirectory || dataTreeView.SelectedNode.Tag is WzFile)
			{

				WzDirectory dir;
				if (dataTreeView.SelectedNode.Tag is WzFile)
				{
					dir = ((WzFile)dataTreeView.SelectedNode.Tag).WzDirectory;
				}
				else
				{
					dir = (WzDirectory)dataTreeView.SelectedNode.Tag;
				}
				string name = dir.Name;
				if (!combineimgs)
				{
					try
					{
						Directory.CreateDirectory(name);
					}
					catch
					{
						name = name + "_";
						Directory.CreateDirectory(name);
					}
				}
				DumpDir(dir, name);
			}
			else
			{
				MessageBox.Show("Please choose a .img or directory");
			}
		}

		private void toolStripTextBox2_TextChanged(object sender, EventArgs e)
		{
			searchidx = 0;
		}

		private void SearchTV(TreeNode parent)
		{
			foreach (TreeNode node in parent.Nodes)
			{
				if (node.Text.Contains(toolStripTextBox2.Text))
				{
					if (currentidx == searchidx)
					{
						dataTreeView.SelectedNode = node;
						node.EnsureVisible();
						dataTreeView.Focus();
						finished = true;
						searchidx++;
						return;
					}
					else
					{
						currentidx++;
					}
				}
				if (node.Nodes.Count != 0)
				{
					SearchTV(node);
					if (finished)
						return;
				}
			}
		}

		private void toolStripButton1_Click(object sender, EventArgs e)
		{
		tryagain:
			finished = false;
			currentidx = 0;
			foreach (TreeNode node in dataTreeView.Nodes)
			{
				if (node.Text.Contains(toolStripTextBox2.Text))
				{
					if (currentidx == searchidx)
					{
						dataTreeView.SelectedNode = node;
						node.EnsureVisible();
						dataTreeView.Focus();
						finished = true;
						searchidx++;
						return;
					}
					else
					{
						currentidx++;
					}
				}
				if (node.Nodes.Count != 0)
				{
					SearchTV(node);
					if (finished)
						return;
				}
			}
			if (searchidx != 0)
			{
				searchidx = 0;
				goto tryagain;
			}
		}

		private void toolStripButton2_Click(object sender, EventArgs e)
		{
			searchidx = 0;
		}

		private void searchToolStripMenuItem_Click(object sender, EventArgs e)
		{
			toolStrip1.Visible = true;
			toolStripTextBox2.Focus();
		}

		private void toolStripButton3_Click(object sender, EventArgs e)
		{
			toolStrip1.Visible = false;
		}

		private void toolStripTextBox2_KeyUp(object sender, KeyEventArgs e)
		{
			if (e.KeyCode == Keys.Enter)
			{
				toolStripButton1_Click(null, null);
			}
		}

		private void unloadToolStripMenuItem_Click(object sender, EventArgs e)
		{
			foreach (WzFile f in WzFiles)
			{
				f.Dispose();
			}
			WzFiles = new List<WzFile>();
			dataTreeView.Nodes.Clear();
		}
	}
}